%% Author: Administrator
%% Created: 2009-3-9
%% Description: TODO: Add description to brownian_particle
-module(map_ranger).

-record(ranger_state, {
            ticker, 
            x, y, 
            level, 
            address, action, 
            count=0, 
            sum_time=0, % microsecond 
            sum_size=0, % bytes
            ox, oy}).
%%
%% Include files
%%
-include("map.hrl").
-include("e2d.hrl").
-include("bench.hrl").

%%
%% Exported Functions
%%
-export([start/1, start/3]).
-compile(export_all).

-define(TIME_INTERVAL, 10). % 10(ms)
-define(START_LEVEL, 11).
-define(CELL_STEP, 1).

-define(MAP_TOP, 39.96870074151587).
-define(MAP_BOTTOM, 39.86547951039044).
-define(MAP_LEFT, 116.31500244140625).
-define(MAP_RIGHT, 116.46881103515625).

-define(MAP_CENTER_X, (?MAP_LEFT + ?MAP_RIGHT) / 2). % 116.3671875
-define(MAP_CENTER_Y, (?MAP_TOP + ?MAP_BOTTOM) / 2). % 39.909205435

%%
%% API Functions
%%
start(Level) ->
    start(?MAP_CENTER_X, ?MAP_CENTER_Y, Level).

start(X, Y, Level) ->
    RandomTimeSwing = random:uniform(?TIME_INTERVAL), 
    Pid = spawn_link(fun() -> start_loop(X, Y, Level, RandomTimeSwing) end),
    Pid.

start_loop(X, Y, Level, TimeSwing) ->
    timer:sleep(TimeSwing),
    Key = geo_util:get_quadtree_address(X, Y, Level),
    Fun = fun(K) ->
                  Begin = now(),
                  ImageSize = get_cell(K),  %get_image(K),
                  Time = timer:now_diff(now(), Begin),
                  {Time, ImageSize} % {microseconds, bytes}
%                  {Time, ImageSize} = timer:tc(?MODULE, get_image, [K])
          end,
    %           #db_io_testdata{count=Count, runtime=SumTime/1000, data_size=SumSize} = 
    Self = self(),
    process_flag(trap_exit, true),
    {ok, TRef} = timer:send_interval(?TIME_INTERVAL, Self, tick),
    loop(#ranger_state{ticker=TRef, x=X, y=Y, level=Level, address=Key, action=Fun, ox=X, oy=Y}).

get_cell(Id) ->
    <<Key:128>> = erlang:md5(Id),
%    io:format("key: ~p~n", [Key]),
    case ring_accessor:get_from_ring(Key) of
        #cell{r_layer=RasterTile} ->
            size(RasterTile#raster_tile.data);
        Any ->
            exit(io_lib:format("cannnot get cell from ring_node, error: ~p", [Any]))
    end.

get_image(Id) ->
    <<Key:128>> = erlang:md5(Id),
    case e2d_server:get({image, Key}) of
        {ok, [{_Context, RasterTile}]} ->
            size(RasterTile#raster_tile.data);
        {ok, _Values = [FirstImg|_]} ->
            size(FirstImg#raster_tile.data);
        {error, {_T, _R}} ->
            exit("e2d_server get error!");
        {error, timeout} ->
            0; %exit("E2D_server timeout");
        Any ->
            io:format("esd_server:get eRR: ~p~n", [Any])
    end.

get_image_by_store(Id) ->
    <<Key:128>> = erlang:md5(Id),
    case e2d_store:get({image, Key}) of
        {error, {_T, _R}} ->
            exit("e2d_server get error!");
        {error, timeout} ->
            0; %exit("E2D_server timeout");
        {ok, {_Context, RasterTile}} ->
            size(RasterTile#raster_tile.data);
        _Any ->
            io:format("esd_server:get ErR: ~p~n", [Id])
    end.

get_image_by_dirty(Id) ->
    <<Key:128>> = erlang:md5(Id),
    case mnesia:dirty_read(gis_data, Key) of
        [{gis_data, Key, _, Cell}] ->
            RasterTile = Cell#cell.r_layer,
            size(RasterTile#raster_tile.data);
        _Any ->
            io:format("esd_server:get ERr: ~p~n", [Id])
    end.

get_image_by_tx(Id) ->
    <<Key:128>> = erlang:md5(Id),
    F = fun() ->
    case catch mnesia:read({gis_data, Key}) of
        [{gis_data, Key, _, Cell}] ->
            RasterTile = Cell#cell.r_layer,
            size(RasterTile#raster_tile.data);
        _Any ->
            io:format("esd_server:get ERr: ~p~n", [Id])
    end
    end,
    {atomic, Result} = mnesia:transaction(F),
    Result.

%    ?Debug("id: ~s, value length=~p~n", [Id, ]).
   

%%
%% Local Functions
%%
loop(#ranger_state{action=Fun} = State) ->
    receive
        stop ->
            #ranger_state{ticker=TRef} = State,
            if
                TRef =/= undefined ->
                    timer:cancel(TRef);
                true ->
                    ok
            end,
            stoped;
%            io:format("\ncount: ~p, elapse time: ~p(ns), average: ~.1f(ns); translate data: ~.1fKB, average ~.1fKB~n", 
%                      [Count, SumTime, SumTime/Count, DataSize, DataSize/Count]);
        {collect_data, From} ->
            DataSize = State#ranger_state.sum_size/1024.0, % KB
            TD = #db_io_testdata{
                        count=State#ranger_state.count, 
                        runtime=State#ranger_state.sum_time/1000, % ms
                        data_size=DataSize
            },
%            io:format("COLLECT DATA: ~p~n", [now()]),
%            bench_server:send_testdata(TD),
            From ! TD,
            loop(State#ranger_state{count=0, sum_time=0, sum_size=0});
        report_state ->
            #ranger_state{count=Count, sum_time=SumTime, sum_size=SumSize} = State,
            DataSize = SumSize/1024.0,
 %           #db_io_testdata{count=State#ranger_state.count, runtime=State#ranger_state.sum_time/1000, data_size=DataSize},
            io:format("\ncount: ~p, elapse time: ~p(ns), average: ~.1f(ns); translate data: ~.1fKB, average ~.1fKB~n", 
                      [Count, SumTime, SumTime/Count, DataSize, DataSize/Count]),
            loop(State);
        {update_testdata, {Time, ImageSize}} ->
            #ranger_state{count=Count, sum_time=SumTime, sum_size=SumSize} = State,
 %          io:format("time: ~p, size: ~p~n", [Time, ImageSize]),
            loop(State#ranger_state{count=Count+1, sum_time=SumTime+Time, sum_size=SumSize+ImageSize});
        tick ->
            AdjustState = adjust_ranger(State),
            #ranger_state{x=X, y=Y, level=Level, address=Key} = AdjustState,
            Pid = self(),
            _FunPid = spawn(fun() -> {Time, ImageSize} = Fun(Key), Pid ! {update_testdata, {Time, ImageSize}} end),
%            timer:apply_after(2 *?TIME_INTERVAL, erlang, exit, [FunPid, kill]),
 %          io:format("time: ~p, size: ~p~n", [Time, ImageSize]),
            #zone{left=Left, right=Right, top=Top, bottom=Bottom} =
                  geo_util:get_coordinates_from_address(Key),
            NewX = X + random_step(Right-Left),
            NewY = Y + random_step(Top-Bottom),
            NewKey = geo_util:get_quadtree_address(X, Y, Level),
            loop(AdjustState#ranger_state{x=NewX, y=NewY, address=NewKey});
        {'EXIT', _Pid, normal} ->
            loop(State);
        {'EXIT', _Pid, {timeout, _Reason}} ->
            loop(State);
        Any ->
            io:format("soMEthinG: ~p~n", [Any]),
            loop(State)
    end.

adjust_ranger(#ranger_state{ox=OX, oy=OY, level=Level, x=X, y=Y}=OldState) when X > ?MAP_RIGHT; X < ?MAP_LEFT; Y > ?MAP_TOP; Y < ?MAP_BOTTOM ->
%    io:format("been out-of-bounds, goto original point~n", []),
    Key = geo_util:get_quadtree_address(OX, OY, Level),
    OldState#ranger_state{x=OX, y=OY, address=Key};
adjust_ranger(#ranger_state{} = State) ->
    State.

random_step(D) ->
    ?CELL_STEP * D * random().

random() ->
    random:uniform() - 0.5.       